package com.zbc.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zbc.domain.entity.Picture;
import com.zbc.domain.entity.User;
import com.zbc.domain.file.UploadPictureResult;
import com.zbc.domain.request.picture.PictureEditRequest;
import com.zbc.domain.request.picture.PictureQueryRequest;
import com.zbc.domain.request.picture.PictureReviewRequest;
import com.zbc.domain.request.picture.PictureUploadRequest;
import com.zbc.domain.vo.PictureVO;
import com.zbc.domain.vo.UserVO;
import com.zbc.enums.ErrorCode;
import com.zbc.enums.PictureReviewStatusEnum;
import com.zbc.exception.BusinessException;
import com.zbc.manager.FileManager;
import com.zbc.mapper.PictureMapper;
import com.zbc.service.PictureService;
import com.zbc.service.UserService;
import com.zbc.utils.ThrowUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author fangyu
 * @version v1.0.0
 * @since 2025/9/2 14:17
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PictureServiceImpl extends ServiceImpl<PictureMapper, Picture> implements PictureService {
    private final FileManager fileManager;
    private final UserService userService;


    /**
     * PictureVO 转 Picture
     *
     * @param pictureVO pictureVO
     * @return Picture
     */
    @Override
    public Picture getPicture(PictureVO pictureVO) {
        if (pictureVO == null) {
            return null;
        }
        Picture picture = new Picture();
        BeanUtil.copyProperties(pictureVO, picture);
        List<String> tagsList = pictureVO.getTags();
        String tags = JSONUtil.toJsonStr(tagsList);
        picture.setTags(tags);
        return picture;
    }

    /**
     * Picture 转 PictureVO
     *
     * @param picture picture
     * @return PictureVO
     */
    @Override
    public PictureVO getPictureVO(Picture picture) {
        if (picture == null) {
            return null;
        }
        PictureVO pictureVO = new PictureVO();
        BeanUtil.copyProperties(picture, pictureVO);
        String jsonTags = picture.getTags();
        List<String> tagsList = JSONUtil.toList(jsonTags, String.class);
        pictureVO.setTags(tagsList);
        return pictureVO;
    }

    @Override
    public PictureVO uploadPicture(MultipartFile multipartFile, PictureUploadRequest uploadRequest, User loginUser) {
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR);
        // 用于判断是新增还是更新图片
        Long pictureId = null;
        if (uploadRequest != null) {
            pictureId = uploadRequest.getId();
        }
        // 如果是更新图片，需要校验图片是否存在
        if (pictureId != null) {
            Picture oldPicture = this.getById(pictureId);
            ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR, "图片不存在");
            // 仅本人或管理员可编辑
            if (!oldPicture.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
            }
        }
        // 上传图片, 得到信息
        // 按照用户 id 划分目录
        String uploadPathPrefix = String.format("public/%s", loginUser.getId());
        UploadPictureResult uploadPictureResult = fileManager.uploadPicture4Minio(multipartFile, uploadPathPrefix);
        // 构造要入库的图片信息
        Picture picture = getPicture(loginUser, uploadPictureResult);
        // 补充审核参数
        this.fillReviewParams(picture, loginUser);
        // 如果 pictureId 不为空, 表示更新, 否则是新增
        if (pictureId != null) {
            // 如果是更新, 需要补充 id 和编辑时间
            picture.setId(pictureId);
            picture.setEditTime(new Date());
        }
        boolean result = this.saveOrUpdate(picture);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "图片上传失败");
        return this.getPictureVO(picture);
    }

    @NotNull
    private static Picture getPicture(User loginUser, UploadPictureResult uploadPictureResult) {
        Picture picture = new Picture();
        picture.setUrl(uploadPictureResult.getUrl());
        picture.setName(uploadPictureResult.getPicName());
        picture.setPicSize(uploadPictureResult.getPicSize());
        picture.setPicWidth(uploadPictureResult.getPicWidth());
        picture.setPicHeight(uploadPictureResult.getPicHeight());
        picture.setPicScale(uploadPictureResult.getPicScale());
        picture.setPicFormat(uploadPictureResult.getPicFormat());
        picture.setUserId(loginUser.getId());
        return picture;
    }

    /**
     * 删除图片
     *
     * @param pictureId 图片 id
     * @param loginUser 当前登录用户
     */
    @Override
    public void deletePicture(long pictureId, User loginUser) {
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR);
        ThrowUtils.throwIf(pictureId <= 0, ErrorCode.PARAMS_ERROR);
        // 判断图片是否存在
        Picture oldPicture = this.getById(pictureId);
        ThrowUtils.throwIf(ObjUtil.isNull(oldPicture), ErrorCode.NOT_FOUND_ERROR, "图片不存在");
        // 删除图片
        boolean removed = this.removeById(pictureId);
        ThrowUtils.throwIf(!removed, ErrorCode.OPERATION_ERROR, "删除图片失败");
    }

    /**
     * 图片校验规则
     *
     * @param picture 图片
     */
    @Override
    public void validPicture(Picture picture) {
        if (picture == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long pictureId = picture.getId();
        String pictureUrl = picture.getUrl();
        String introduction = picture.getIntroduction();
        ThrowUtils.throwIf(pictureId <= 0, ErrorCode.PARAMS_ERROR);
        if (StrUtil.isNotBlank(pictureUrl)) {
            ThrowUtils.throwIf(pictureUrl.length() > 1024, ErrorCode.PARAMS_ERROR, "图片链接过长");
        }
        if (StrUtil.isNotBlank(introduction)) {
            ThrowUtils.throwIf(introduction.length() > 1024, ErrorCode.PARAMS_ERROR, "图片简介过长");
        }
    }

    /**
     * 封装图片的分页查询条件
     *
     * @param request 查询参数
     * @return 查询条件
     */
    @Override
    public QueryWrapper<Picture> getQueryWrapper(PictureQueryRequest request) {
        QueryWrapper<Picture> wrapper = new QueryWrapper<>();
        if (request == null) {
            return wrapper;
        }
        // 1. 获取查询参数
        Long id = request.getId();
        String name = request.getName();
        String introduction = request.getIntroduction();
        String category = request.getCategory();
        List<String> tags = request.getTags(); // 单独处理
        Long picSize = request.getPicSize();
        Integer picWidth = request.getPicWidth();
        Integer picHeight = request.getPicHeight();
        Double picScale = request.getPicScale();
        String picFormat = request.getPicFormat();
        String searchText = request.getSearchText(); // 单独处理
        Long userId = request.getUserId();
        Long spaceId = request.getSpaceId();
        boolean nullSpaceId = request.isNullSpaceId();
        Integer reviewStatus = request.getReviewStatus();
        String reviewMessage = request.getReviewMessage();
        Long reviewerId = request.getReviewerId();
        Date startEditTime = request.getStartEditTime();
        Date endEditTime = request.getEndEditTime();
        String sortField = request.getSortField();
        String sortOrder = request.getSortOrder();
        // 2. 封装查询条件
        wrapper.eq(id != null && id > 0, "id", id);
        wrapper.like(StrUtil.isNotBlank(name), "name", name);
        wrapper.like(StrUtil.isNotBlank(introduction), "introduction", introduction);
        wrapper.eq(StrUtil.isNotBlank(category), "category", category);
        wrapper.eq(ObjUtil.isNotEmpty(picSize), "pic_size", picSize);
        wrapper.eq(ObjUtil.isNotEmpty(picWidth), "pic_width", picWidth);
        wrapper.eq(ObjUtil.isNotEmpty(picHeight), "pic_height", picHeight);
        wrapper.eq(ObjUtil.isNotEmpty(picScale), "pic_scale", picScale);
        wrapper.like(StrUtil.isNotBlank(picFormat), "pic_format", picFormat);
        wrapper.eq(userId != null && userId > 0, "user_id", userId);
        wrapper.eq(spaceId != null && spaceId > 0, "space_id", spaceId);
        wrapper.eq(ObjUtil.isNotEmpty(reviewStatus), "review_status", reviewStatus);
        wrapper.like(StrUtil.isNotBlank(reviewMessage), "review_message", reviewMessage);
        wrapper.eq(reviewerId != null && reviewerId > 0, "reviewer_id", reviewerId);
        wrapper.ge(ObjUtil.isNotEmpty(startEditTime), "startEditTime", startEditTime);
        wrapper.lt(ObjUtil.isNotEmpty(endEditTime), "endEditTime", endEditTime);
        wrapper.isNull(nullSpaceId, "space_id"); // 为空表示查询公共图库
        // tags 单独处理, 按 "标签1" 搜索
        if (CollUtil.isNotEmpty(tags)) {
            for (String tag : tags) {
                wrapper.like("tags", "\"" + tag + "\"");
            }
        }
        // 名称和简介可以同时搜
        if (StrUtil.isNotBlank(searchText)) {
            // where (name like '%searchText%' or introduction like '%searchText%') and isDelete = 0;
            wrapper.and(i -> i.like("name", searchText))
                    .or().like("introduction", searchText);
        }
        // 排序
        wrapper.orderBy(StrUtil.isNotBlank(sortField), sortOrder.equals("ascend"), sortField);
        return wrapper;
    }

    /**
     * 获取图片的 VO(处理 UserVO)
     *
     * @param picture 图片
     * @return 图片的 VO
     */
    @Override
    public PictureVO getPictureVOWithUserVO(Picture picture) {
        if (picture == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        PictureVO pictureVO = this.getPictureVO(picture);
        Long userId = picture.getUserId();
        if (userId != null && userId > 0) {
            User user = userService.getById(userId);
            UserVO userVO = userService.getUserVO(user);
            pictureVO.setUserVO(userVO);
        }
        return pictureVO;
    }

    /**
     * 分页获取图片的 VO (处理 UserVO)
     *
     * @param picturePage 图片的分页数据
     * @return 图片的 VO
     */
    @Override
    public Page<PictureVO> getPictureVOPage(Page<Picture> picturePage) {
        // 1. 获取图片列表
        List<Picture> pictureList = picturePage.getRecords();
        // 2. 封装为 VO
        Page<PictureVO> pictureVOPage = new Page<>(picturePage.getCurrent(), picturePage.getSize(), picturePage.getTotal());
        if (CollUtil.isEmpty(pictureList)) {
            return pictureVOPage;
        }
        // 3. 获取 VO 列表
        List<PictureVO> pictureVOList = pictureList.stream().map(this::getPictureVO).collect(Collectors.toList());
        // 4. 获取用户 id 集合
        Set<Long> userIdSet = pictureList.stream().map(Picture::getUserId).collect(Collectors.toSet());
        // 5. 获取用户集合
        List<User> userList = userService.listByIds(userIdSet);
        // 6. (userId, User) 进行分组
        Map<Long, List<User>> userMap = userList.stream().collect(Collectors.groupingBy(User::getId));
        // 7. 遍历填充数据
        for (PictureVO pictureVO : pictureVOList) {
            Long userId = pictureVO.getUserId();
            User user = null;
            if (userMap.containsKey(userId)) {
                user = userMap.get(userId).get(0);
            }
            UserVO userVO = userService.getUserVO(user);
            pictureVO.setUserVO(userVO);
        }
        pictureVOPage.setRecords(pictureVOList);
        return pictureVOPage;
    }

    /**
     * 用户图片编辑
     *
     * @param request   图片编辑请求
     * @param loginUser 登录用户
     */
    @Override
    public void editPicture(PictureEditRequest request, User loginUser) {
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR);
        // 1. 属性拷贝
        Picture picture = new Picture();
        BeanUtils.copyProperties(request, picture);
        // 2. 注意tags字段
        List<String> tagsList = request.getTags();
        String tags = JSONUtil.toJsonStr(tagsList);
        picture.setTags(tags);
        // 3. 设置编辑时间
        picture.setEditTime(new Date());
        // 4. 数据检验(合法性: url、简介等)
        this.validPicture(picture);
        // 5. 判断图片是否存在
        Picture oldPicture = this.getById(picture.getId());
        if (oldPicture == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "图片不存在");
        }
        // 6. 补充审核参数
        this.fillReviewParams(picture, loginUser);
        // 7. 修改图片
        boolean updated = this.updateById(picture);
        if (!updated) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "修改图片失败");
        }
    }

    /**
     * 管理员审核功能
     *
     * @param request   审核请求
     * @param loginUser 登录用户
     */
    @Override
    public void dePictureReview(PictureReviewRequest request, User loginUser) {
        // 1. 参数校验
        Long id = request.getId();
        Integer reviewStatus = request.getReviewStatus();
        PictureReviewStatusEnum reviewStatusEnum = PictureReviewStatusEnum.getEnumByValue(reviewStatus);
        if (id <= 0 || reviewStatusEnum == null || PictureReviewStatusEnum.REVIEWING.equals(reviewStatusEnum)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 2. 判断图片是否存在
        Picture oldPicture = this.getById(id);
        if (oldPicture == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "图片不存在");
        }
        // 3. 不能重复审核
        if (oldPicture.getReviewStatus().equals(reviewStatus)) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "不能重复审核");
        }
        // 4. 执行审核状态
        Picture updatePicture = new Picture();
        BeanUtil.copyProperties(request, updatePicture);
        // 设置审核状态
        updatePicture.setReviewerId(loginUser.getId());
        updatePicture.setReviewTime(new Date());
        boolean updated = this.updateById(updatePicture);
        if (!updated) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "更新失败");
        }
    }

    /**
     * 补充审核参数
     *
     * @param picture   图片
     * @param loginUser 当前登录用户
     */
    @Override
    public void fillReviewParams(Picture picture, User loginUser) {
        ThrowUtils.throwIf(picture == null, ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR);
        if (userService.isAdmin(loginUser)) {
            // 当前登录用户为管理员, 自动过审
            picture.setReviewStatus(PictureReviewStatusEnum.PASS.getValue());
            picture.setReviewMessage("管理员自动过审");
            picture.setReviewerId(loginUser.getId());
            picture.setReviewTime(new Date());
        } else {
            // 普通用户, 需要进行审核
            picture.setReviewStatus(PictureReviewStatusEnum.REVIEWING.getValue());
        }
    }
}
